/* 
 * webpack 4 打包
*/

import {_, difference} from './utils/underscore';

/* ********** 求数组的最大值和最小值 ********** */

/**
 * Math.max
 * Math.max(val1, val2, ...)
 * 1.如果有任一参数不能转换为数值，结果为NaN
 * 2.max是Math的静态方法，不是实例方法（就是不使用 new）
 * 3.如果没有参数，则结果为 -Infinity（-无穷大）
 */

// var min = Math.min(); // Infinity
// var max = Math.max(); // -Infinity

// console.log(min > max); // true

// // 原始方法，循环遍历
// var arr = [6, 4, 1, 8, 2, 11, 23];

// var result = arr[0];
// for (var i = 1; i < arr.length; i++) {
//   result = Math.max(result, arr[i]);
// }

// console.log('循环最大值', result);

// // reduce方法
// function max (prev, next) {
//   return Math.max(prev, next);
// }
// console.log('reduce最大值', arr.reduce(max))

// // 排序
// var arr2 = arr.concat();
// arr2.sort((a, b) => a - b);
// console.log('排序最大值', arr2[arr2.length - 1]);

// // eval
// var evalMax = eval('Math.max(' + arr +')');
// console.log('eval最大值', evalMax);

// // apply
// console.log('apply 最大值', Math.max.apply(null, arr));
// // 参数展开
// console.log('...最大值', Math.max(...arr));

// 原始方法，循环遍历
// reduce方法
// 排序
// eval
// apply
// 参数展开

/* ********** 数组扁平化 ********** */

// var arr = [[1, 5, 6], 7, [2, [3, 4]]];

// 递归
// function flatten (arr) {
//   var result = [];
//   for (var i = 0; i < arr.length; i++) {
//     if (Array.isArray(arr[i])) {
//       result = result.concat(flatten(arr[i]));
//     } else {
//       result.push(arr[i]);
//     }
//   }
//   return result;
// }

// toString 元素都是数字可以使用，使用场景非常有限
// function flatten (arr) {
//   return arr.toString().split(',').map(function (item) {
//     return +item;
//   });
// }

// reduce
// function flatten (arr) {
//   return arr.reduce(function (prev, next) {
//     return prev.concat(Array.isArray(next) ? flatten(next) : next);
//   }, []);
// }

// ES6 ...
// function flatten (arr) {
//   while (arr.some(item => Array.isArray(item))) {
//     arr = [].concat(...arr);
//   }
//   return arr;
// }

// underscore
/**
 * 数组扁平化
 * @param {Array} input         要处理的数组
 * @param {Boolean} shallow     是否只扁平化一层
 * @param {Boolean} strict      是否严格处理元素
 * @param {Array} output        为了方便递归而递归的参数
 */
// function flatten (input, shallow, strict, output) {
//   // 递归使用的时候会用到output
//   output = output || [];
//   var idx = output.length;

//   for (var i = 0, len = input.length; i < len; i++) {
//     var value = input[i];
//     // 如果是数组，就进行处理
//     if (Array.isArray(value)) {
//       // 如果是只扁平一层，遍历该数组，依次填入output
//       if (shallow) {
//         var j = 0;
//         var length = value.length;
//         while (j < length) output[idx++] = value[j++];
//       }
//       // 如果是全部扁平就递归，传入已经处理的output，递归中接着处理output
//       else {
//         flatten(value, shallow, strict, output);
//         idx = output.length;
//       }
//     }
//     // 不是数组，根据strict的值判断是跳过不处理还是放入output
//     else if (!strict) {
//       output[idx++] = value;
//     }
//   }

//   return output;
// }


// console.log(_.union([1,2,3], [101, 2, 1, 10], 4, 5));
// console.log(difference([1,2,3,4,5], [5,2,10], [4], 3))


/* ********** 在数组中查找执行元素 ********** */

var arr = [12, 5, 8, 130, 5, 44];

/**
 * ES6 findIndex
 * 找出第一个符合条件的下标
 */
// console.log(arr.findIndex(item => item > 15));

/**
 * 实现一个findIndex
 * @param {Array} array 数组
 * @param {Function} predicate 条件函数
 * @param {Object} context 执行上下文
 */
// function findIndex (array, predicate, context) {
//   for (var i = 0; i < array.length; i++) {
//     if (predicate.call(context, array[i], i, array)) return i;
//   }
//   return -1;
// }

// console.log(findIndex(arr, item => item === 8));

// findLastIndexOf
/**
 * 实现findLastIndexOf
 * @param {Array} array 数组
 * @param {Function} predicate 条件函数
 * @param {Object} context 执行上下文
 */
// function findLastIndexOf (array, predicate, context) {
//   var length = array.length;
//   for (var i = length; i >= 0; i--) {
//     if (predicate.call(context, array[i], i, array)) return i;
//   }
//   return -1;
// }

// console.log(findLastIndexOf(arr, item => item > 100));

// createIndexFinder
/**
 * 创建一个查找方法
 * @param {Number} dir 步长
 */
// function createIndexFinder (dir) {
//   return function (array, predicate, context) {
//     var length = array.length;
//     var index = dir > 0 ? 0 : length - 1;
//     for (; index >= 0 && index < length; index += dir) {
//       if (predicate.call(context, array[index], index, array)) return index;
//     }
//     return -1;
//   }
// }

// var findIndex = createIndexFinder(1);
// var findLastIndex = createIndexFinder(-1);

// console.log(findIndex(arr, item => item ===5));
// console.log(findLastIndex(arr, item => item ===5));

// sortedIndex
// 在一个排好序的数组中找到 value 对应的位置，保证插入数组后，依然保持有序的状态

// var arr2 = [10, 20, 20, 30, 40, 50];
// 第一版 二分查找
// function sortedIndex (array, obj) {
//   var low = 0;
//   var high = array.length;
//   while (low < high) {
//     var mid = Math.floor((low + high) / 2);
//     if (array[mid] < obj) low = mid + 1;
//     else high = mid;
//   }
//   return high;
// }

// console.log(sortedIndex(arr2, 21), arr2);

// 第二版
// var stooges = [{name: 'stooge1', age: 10}, {name: 'stooge2', age: 30}];

// function cb (func, context) {
//   if (func === void 0) return func;
//   return function () {
//     return func.apply(context, arguments);
//   }
// }

// function sortedIndex (array, obj, iteratee, context) {
//   iteratee = cb(iteratee, context);

//   var low = 0;
//   var high = array.length;
//   while (low < high) {
//     var mid = Math.floor((low + high) / 2);
//     if (iteratee(array[mid]) < iteratee(obj)) low = mid + 1;
//     else high = mid;
//   }
//   return high;
// }

// console.log(sortedIndex(stooges, {name: '123', age:31}, item => item.age))

// indexOf
// 第一版
// function createIndexOfFinder (dir) {
//   return function (array, item) {
//     var length = array.length;
//     var index = dir > 0 ? 0 : length - 1;
//     for (; index >= 0 && index < length; i += dir) {
//       if (array[index] === item) return index;
//     }
//     return -1;
//   }
// }

// var indexOf = createIndexOfFinder(1);
// var lastIndexOf = createIndexOfFinder(-1);

// 第二版，加入 fromIndex
// 设定开始查找的位置。如果该索引值大于或等于数组长度，意味着不会在数组里查找，返回 -1。如果参数中提供的索引值是一个负值，则将其作为数组末尾的一个抵消，即 -1 表示从最后一个元素开始查找，-2 表示从倒数第二个元素开始查找 ，以此类推。 注意：如果参数中提供的索引值是一个负值，仍然从前向后查询数组。如果抵消后的索引值仍小于 0，则整个数组都将会被查询。其默认值为 0。
// function createIndexOfFinder (dir, predicate, sortedIndex) {
//   return function (array, item, idx) {
//     var length = array.length;
//     var i = 0;
//     if (typeof idx === 'number') {
//       if (dir > 0) {
//         // 正向
//         i = idx >= 0 ? idx : Math.max(length + idx, 0);
//       } else {
//         // 反向
//         length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
//       }
//     }
//     else if (sortedIndex && idx && length) {
//       idx = sortedIndex(array, item);
//       // 如果该插入的位置的值正好等于元素的值，说明是第一个符合要求的值
//       return array[idx] === item ? idx : -1;
//     }

//     // 判断元素是否是 NaN
//     if (item !== item) {
//       // 在截取好的数组中查找第一个满足isNaN函数的元素下标
//       idx = predicate(array.slice(i, length), isNaN);
//       return idx >= 0 ? idx + i : -1;
//     }

//     for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
//       if (array[idx] === item) return idx;
//     }
//     return -1;
//   }
// }

// var indexOf = createIndexOfFinder(1, findIndex, sortedIndex);
// var lastindexOf = createIndexOfFinder(-1, findLastIndex);

// 实现一个each方法

/**
 * 判断数据类型方法 typeof + Object.prototype.toString.call()
 */
var class2type = {};
// 生成 class2type 映射
'Boolean Number String Function Array Date RegExp Error Null Undefined'.split(' ').map(item => {
  class2type[`[object ${item}]`] = item.toLowerCase();
})

export function type (obj) {
  // 解决IE6 null也是object的问题
  if (obj === null) {
    return obj + '';
  }
  return typeof obj === 'object' || typeof obj === 'function' ? class2type[Object.prototype.toString.call(obj)] || 'object' : typeof obj;
}

/**
 * 判断是否是window对象
 * @param {Object} obj 
 */
export function isWindow (obj) {
  return obj !== null && obj === obj.window;
}

// isArrayLike
/**
 * 判断是否是数组e
 * @param {Array} obj 
 */
export function isArrayLike (obj) {
  // obj 必须有 length 属性
  var length = !!obj && 'length' in obj && obj.length;
  var typeRes = type(obj);

  // 排除掉函数和 Window 对象
  if (typeRes === 'function' || isWindow(obj)) {
    return false;
  }

  return typeRes === 'array' || length === 0 || typeof length === 'number' && length > 0 && (length - 1) in obj;
}

// 第一版
// function each (obj, callback) {
//   var i = 0;
//   var length = obj.length;
//   if (isArrayLike(obj)) {
//     for (; i < length; i++) {
//       if (callback.call(obj[i], obj[i], i) === false) break;
//     }
//   }
//   else {
//     for (i in obj) {
//       if (callback.call(obj[i], obj[i], i) === false) break;
//     }
//   }
//   return obj;
// }
// var arr = [4,5,6,7];
// var json = {a: 12, b: 5}

// console.log(each(arr, (item, index) => {
//   console.log(index, item);
// }));
// console.log(each(json, (val, key) => {
//   console.log(`${key}=${val}`);
// }));

// var person = [
//   {name: 'zhouyang'},
//   {name: 'chendan'}
// ];

// console.log(each(person, function (item, index) {
//   this.age = 18 + index;
// }))

// var arr = Array.from({length}, (v, i) => i);
// console.time('for')
// var i = 0;
// for (; i < arr.length; i++) {
//   i += arr[i]
// }
// console.timeEnd('for')

// console.time('each');
// var j = 0;
// each(arr, function (item, index) {
//   j += item;
// })
// console.timeEnd('each');
